<?php
/**
 * FlickrPhotosets class file
 * @package FlickrPhotosets
 */

/**
 * This is the base class of all classes in the FlickrPhotosets package.
 * 
 * It provides any functions that are common among child classes. This class handles
 * the basic functions of the app, including connecting to the database, creating
 * the Facebook object, and creating the Flickr object.
 * 
 * @package FlickrPhotosets
 * @author Brad Dougherty <bdougherty@gmail.com>
 * @version 7.10.29
 * @license http://www.gnu.org/licenses/gpl.html GNU General Public License 3.0
 * @since Class available since Release 2.0
 */
abstract class FlickrPhotosets {
	
	/**
	 * The user's Flickr Photosets configuration
	 * @var array
	 */
	public $config;
	
	/**
	 * The database object
	 * @var FlickrPhotosetsDatabase
	 */
	protected $db;
	
	/**
	 * Debug mode
	 * @var boolean
	 */
	public $debug = false;
	
	/**
	 * The Facebook object
	 * @var Facebook
	 */
	public $facebook;
	
	/**
	 * The Flickr object
	 * @var phpFlickr
	 */
	public $flickr;
	
	/**
	 * The user's Flickr information
	 * @var array
	 */
	public $flickr_user;
	
	/**
	 * The application logger
	 * @var FlickrPhotosetsLogger
	 */
	public $log;
	
	/**
	 * The Flickr ID
	 * @var string
	 */
	public $nsid;
	
	/**
	 * The user's Facebook ID
	 * @var string
	 */
	public $uid;
	
	/**
	 * The viewing user's Facebook ID
	 * @var string
	 */
	public $viewer_uid;
	
	/**
	 * Creates the Facebook object and connects to the database.
	 * 
	 * @author Brad Dougherty <bdougherty@gmail.com>
	 */
	public function __construct() {
		// Set up the Facebook client
		$this->facebook = new Facebook(FACEBOOK_API_KEY, FACEBOOK_API_SECRET);
		$this->viewer_uid = $this->facebook->user;
		
		// Initiate the connection to the database
		$this->connect_db();
		
		// Create the logger
		$this->log = new FlickrPhotosetsLogger($this->db);
		
		// Set debug mode
		if (isset($_GET['debug']) and $this->viewer_uid = '1470030056') {
			$this->debug = true;
		}
	}
	
	/**
	 * Checks for new Flickr photos and sends a notification.
	 * 
	 * This function will pull from the last 75 public photos to determine
	 * if any new photos have been uploaded. Regardless of that determination,
	 * it will set the last photo in the photostream as the last photo in
	 * the database. It will return true if the notification was sent,
	 * false otherwise.
	 * 
	 * @author Brad Dougherty <bdougherty@gmail.com>
	 * @return boolean True if the notification was sent properly
	 */
	protected function check_for_new_photos() {
		
		// Only do this if the owner is viewing
		if ($this->viewer_uid == $this->uid) {
			
			// Flickr not authorized
			if (!$this->is_flickr_authorized()) {
				return false;
			}

			// News feed notifications disabled
			if(!$this->config['enable_newsfeed']) {
				return false;
			}

			// Get the latest 75 photos
			$photos = $this->flickr->photos_search(array(
				'user_id'  => $this->nsid,
				'per_page' => '75',
				'page'     => 1,
				'extras'   => 'date_upload',
			));

			// Skip notifications if the last photo in the db is 0
			if ($this->config['last_photo'] != 0) {

				// Search the $latest_photos array for the photo we have stored as last
				$new_photo_key = $this->multi_array_search($this->config['last_photo'], $photos['photo']);

				// If the key is greater than zero, new photos were uploaded
				if ($new_photo_key[0] > 0 or empty($new_photo_key)) {

					// Figure out the number of new photos since the last one
					$num_new_photos = (empty($new_photo_key)) ? count($photos['photo']) : $new_photo_key[0];

					// Cut the array off after the new photos
					$photos['photo'] = array_slice($photos['photo'], 0, $num_new_photos);

					// Randomize, but only if there are more than 4
					$random = array();
					if ($num_new_photos > 4) {
						$random = array_rand($photos['photo'], 4);
						sort($random);
					}
					else {
						for ($i = 0; $i < $num_new_photos; $i++) {
							$random[$i] = $i;
						}
					}

					// Create an array with just the photos we are using in the notification
					$extra = array();
					$images = array();
					$k = 0;
					foreach ($random as $i) {
						$images[$k]['src'] = $this->flickr->buildPhotoUrl($photos['photo'][$i], "square");
						$images[$k]['link'] = $this->app_url.'/photos/'.$this->uid.'/'.$photos['photo'][$i]['id'].'/';
						$extra[$k] = $photos['photo'][$i]['id'];
						$k++;
					}

					// Construct the title string
					$photo_string = ($num_new_photos > 1) ? 'photos' : 'photo';
					$num_new_photos = ($num_new_photos == 1) ? 'a' : $num_new_photos;

					// Publish story using 4 random photos
					try {
						$this->facebook->api_client->feed_publishTemplatizedAction(
							$this->uid,
							'{actor} uploaded new <a href="http://rit.facebook.com/apps/application.php?api_key='.FACEBOOK_API_KEY.'">Flickr {photo}</a>.',
							'{"count":"'.$num_new_photos.'","photo":"'.$photo_string.'"}',
							'',
							'',
							'',
							$images[0]['src'],
							$images[0]['link'],
							$images[1]['src'],
							$images[1]['link'],
							$images[2]['src'],
							$images[2]['link'],
							$images[3]['src'],
							$images[3]['link']
						);
						$this->log->notification($this->uid, 'photos', true, implode(',', $extra));
						
						$success = true; // Message sent
						
						// Update the ID stored in the database
						$this->set_last_photo($photos['photo'][0]['id']);
						
					}
					catch (FacebookRestClientException $e) {
						$this->log->notification($this->uid, 'photos', false, $e->getMessage());
						$success = false; // Caused exception
					}

				}
				else {
					$success = false; // No new photos
				}	

			}
			else {
				$succcess = false; // No last photo was in the database
			}
			
		}
		else {
			$success = false; // Owner not viewing
		}
		
		// Return the success of sending the message
		return $success;
				
	}
	
	/**
	 * Checks for new Flickr sets and sends a notification.
	 *
	 * This function will pull all of the user's photosets to determine
	 * whether or not they have created any new sets. Regardless, it
	 * will set a field in the DB to a comma-separated list of the set
	 * ids to use in the future when it is run. The function returns an
	 * array that contains the IDs of photosets sent if any are, otherwise
	 * it returns false.
	 * 
	 * @author Brad Dougherty <bdougherty@gmail.com>
	 * @return Array with IDs and status of new set notifications,
	 *         false if no notifications sent
	 */
	protected function check_for_new_photosets() {
		
		// Only do this if the owner is viewing
		if ($this->viewer_uid == $this->uid) {
			
			// Flickr not authorized
			if (!$this->is_flickr_authorized()) {
				return false;
			}

			// News feed notifications disabled
			if(!$this->config['enable_newsfeed']) {
				return false;
			}

			// Get the list of sets
			$photosets = $this->flickr->photosets_getList($this->nsid);

			// Only attempt notifications if we have the previous sets in the DB
			if (!empty($this->config['photosets'])) {

				// Explode the stored IDs so we can search for them
				$stored_ids = explode(',', $this->config['photosets']);

				/*
				Go through each photoset returned from Flickr and search for it.
				If we can't find one, send a notification because it's new.
				*/
				$return = array();
				foreach($photosets['photoset'] as $photoset) {

					$result = array_search($photoset['id'], $stored_ids);
					if ($result === false) {

						// Build information for the notification
						$photoset_url = $this->app_url.'/photos/'.$this->uid.'/sets/'.$photoset['id'].'/';

						/*
						Try to send the notification. For each notification attempt,
						we will create an element in the array with the ID and the
						result of the notification.
						*/
						$exception = '';
						try {
							$this->facebook->api_client->feed_publishTemplatizedAction(
								$this->uid,
								'{actor} created a new <a href="http://rit.facebook.com/apps/application.php?api_key='.FACEBOOK_API_KEY.'">Flickr photoset</a>.',
								'',
								'<b><a href="{set_url}">{set_name}</a></b> - {count} photos',
								'{"set_url":"'.$photoset_url.'","set_name":"'.$photoset['title'].'","count":"'.$photoset['photos'].'"}',
								$photoset['description'],
								$this->flickr->buildPhotoUrl($photo, "thumbnail"),
								$photoset_url
							);
							$success = true;
							
							// Create an array with just the IDs
							$photoset_ids = array();
							foreach ($photosets['photoset'] as $photoset) {
								array_push($photoset_ids, $photoset['id']);
							}

							// Update the string stored in the database
							$this->set_photosets(implode(',', $photoset_ids));
							
						}
						catch (FacebookRestClientException $e) {
							$exception = $e->getMessage();
							$success = false;
						}
						array_push($return, array('id' => $photoset['id'], 'notification' => $success, 'exception' => $exception));

					}

				}
				
				// Log each attempt
				foreach ($return as $photoset) {
					$extra = (empty($photoset['exception'])) ? $photoset['id'] : $photoset['id'].':'.$photoset['exception'];
					$this->log->notification($this->uid, 'photosets', $photoset['notification'], $extra);
				}
				
			}

			return (count($return) > 0) ? $return : false;
			
		}
		else {
			return false; // Owner not viewing
		}
		
	}
	
	/**
	 * Initiates the connection to the database.
	 * 
	 * @author Brad Dougherty <bdougherty@gmail.com>
	 * @return boolean True if connected
	 */
	final protected function connect_db() {
		if (DB_TYPE == 'data_store') {
			$this->db = new FlickrPhotosetsDataStore();
		}
		else {
			$this->db = new FlickrPhotosetsMysqlDB(DB_HOST, DB_USER, DB_PASS, DB_NAME, DB_TABLE);
		}
	}
	
	/**
	 * Echoes a statement, but only in debug mode.
	 * 
	 * @author Brad Dougherty <bdougherty@gmail.com>
	 * @param string The strings to echo
	 */
	final public function echo_debug() {
		if ($this->debug) {
			$args = func_get_args();
			foreach ($args as $arg) {
				echo $arg;
			}
		}
	}
	
	/**
	 * Print or echo an error message using FBML.
	 * 
	 * @author Brad Dougherty <bdougherty@gmail.com>
	 * @param array $error An array with the error parameters
	 * @param boolean $return Whether or not to return the error string
	 * @return string The error string (if $echo param set to false)
	 */
	public function error($error, $return = false) {
		// Replace some things if the user is the viewer
		if ($this->uid == $this->viewer_uid) {
			
			// If there is a separate message or title
			$error['title'] = (isset($error['title_you'])) ? $error['title_you'] : $error['title'];
			$error['message'] = (isset($error['message_you'])) ? $error['message_you'] : $error['message'];
			
			// Find some verbs and replace them if the owner is the viewer
			$error['title'] = str_replace('has', 'have', $error['title']);
			$error['message'] = str_replace('has', 'have', $error['message']);
			
			// Replace strings inside #s
			$error['title'] = preg_replace('/#([A-Za-z0-9]+)#([A-Za-z0-9]+)#/', '$2', $error['title']);
			$error['message'] = preg_replace('/#([A-Za-z0-9]+)#([A-Za-z0-9]+)#/', '$2', $error['message']);
			
		}
		else {
			$error['title'] = preg_replace('/#([A-Za-z0-9]+)#([A-Za-z0-9]+)#/', '$1', $error['title']);
			$error['message'] = preg_replace('/#([A-Za-z0-9]+)#([A-Za-z0-9]+)#/', '$1', $error['message']);
		}
		
		// Replace %uid% with the uid
		$error['title'] = str_replace("%uid%", $this->uid, $error['title']);
		$error['message'] = str_replace("%uid%", $this->uid, $error['message']);
		
		// Print the header
		$error_string = '';
		if ($error['with_header']) {
			$error_string .= '<fb:header decoration="add_border">'.$this->app_name.'</fb:header>';
		}
		
		// Print the error
		$error_string .= '<fb:error><fb:message>'.$error['title'].'</fb:message>'.$error['message'].'</fb:error>';
		
		// Echo or return
		if ($return) {
			return $error_string;
		}
		else {
			echo $error_string;
		}
		
		// Die if the error is fatal
		if ($error['fatal']) {
			die();
		}
	}
	
	/**
	 * Gets the google-analytics FBML tag.
	 * 
	 * @author Brad Dougherty <bdougherty@gmail.com>
	 * @param string $page The optional page name
	 * @return string The analytics tag
	 */
	final public function get_analytics($page = '') {
		$analytics = '<fb:google-analytics uacct="'.GOOGLE_ANALYTICS_ACCOUNT.'"';
		$analytics .= (empty($page)) ? '' : ' page="'.$page.'"';
		$analytics .= ' />';
		return $analytics;
	}
	
	/**
	 * Gets the dashboard header.
	 * 
	 * Unfortunately, Facebook does not allow for changes to the header
	 * text of the dashboard header, so I have to print out all of the
	 * html to create it. This function prints the standard header for
	 * pages that are just for users of the app.
	 * 
	 * @author Brad Dougherty <bdougherty@gmail.com>
	 * @param string $title Additional text to add to the title
	 * @return string The dashboard header
	 */
	public function get_dashboard_header($title = '') {
		$dashboard = '
			<div class="dashboard_header">
				<div class="dh_links clearfix">
					<div class="dh_actions">
		';
		
		if ($this->is_flickr_authorized()) {
			if ($this->config['enable_gallery_photostream']) {
				$dashboard .= '<a href="'.$this->app_url.'/photos/'.$this->uid.'/">My Photostream</a><span class="pipe">|</span>';
			}

			if ($this->config['enable_gallery_photosets']) {
				$dashboard .= '<a href="'.$this->app_url.'/photos/'.$this->uid.'/sets/">My Photosets</a><span class="pipe">|</span>';
			}
		}
		
		$dashboard .= '
						<a href="'.$this->app_url.'/configure.php">Configure '.$this->app_name.'</a>
					</div>

					<div class="dh_help">
						<a href="'.$this->app_url.'/help.php">Help</a>
					</div>
				</div>

				<div class="dh_titlebar clearfix">
					<h2 style="background-image: url(http://photos-a.ak.facebook.com/photos-ak-sctm/v43/152/1470030056/app2_1470030056_2360202224_4231.gif)">
		'.$this->app_name;
		
		$dashboard .= (empty($title)) ? '' : ' '.$title ;
					
		$dashboard .= '
					</h2>
				</div>
			</div>
		';
		
		return $dashboard;
	}
	
	/**
	 * Get the developer's session key (it should be infinite)
	 * 
	 * @author Brad Dougherty <bdougherty@gmail.com>
	 * @return string The developer's session key
	 */
	final protected function get_developer_sessionkey() {
		return DEV_SESSION_KEY;
	}
	
	/**
	 * Gets the user's configuration data.
	 * 
	 * This function uses the $uid attribute to determine which user's
	 * configuration information to get.
	 * 
	 * @author Brad Dougherty <bdougherty@gmail.com>
	 * @param string $uid The UID of the user (otherwise used the UID attribute)
	 * @return boolean True if data was retrieved successfully
	 */
	final public function get_info($uid = '') {
		// If the UID param is empty, use the attribute
		if (empty($uid)) {
			$uid = $this->uid;
		}
		
		// What is returned from get_info is the config array
		$return = $this->db->get_info($uid);
		$this->config = $return;
		return ($return === false) ? false : true;
	}
	
	/**
	 * Gets the disclaimer paragraph.
	 * 
	 * @author Brad Dougherty <bdougherty@gmail.com>
	 * @param boolean $border Add the top border and background color, defaults to false
	 * @return string The disclaimer
	 */
	final public function get_disclaimer($border = false) {
		$disclaimer = '<div class="disclaimer clearfix';
		$disclaimer .= ($border) ? ' with_border">' : '">';
 		$disclaimer .= '<p>This product is not created or endorsed by <a href="http://facebook.com">Facebook</a>. This product uses the <a href="http://flickr.com/services/api/">Flickr API</a> but is not endorsed or certified by <a href="http://flickr.com">Flickr</a>. Hosting provided by <a href="http://www.1and1.com/?k_id=6902840">1&1</a>.</p></div>';
		return $disclaimer;
	}
	
	/**
	 * Returns the fb:name tag to show a user's name. Respects the
	 * user's name privacy setting.
	 * 
	 * @author Brad Dougherty <bdougherty@gmail.com>
	 * @deprecated Use get_name_fbml();
	 * @param boolean $use_my Whether or not to replace the name with My
	 * @param boolean $possessive Use the possessive form of the name
	 * @return string The FBML to show the name
	 */
	public function get_name($use_my = false, $possessive = true, $use_you = true) {
		return $this->get_name_fbml($use_my, $possessive, $use_you);
	}
	
	/**
	 * Returns the fb:name tag to show a user's name. Respects the
	 * user's name privacy setting.
	 * 
	 * @author Brad Dougherty <bdougherty@gmail.com>
	 * @param boolean $use_my Whether or not to replace the name with My
	 * @param boolean $possessive Use the possessive form of the name
	 * @return string The FBML to show the name
	 */
	public function get_name_fbml($use_my = false, $possessive = true, $use_you = true) {
		$name = '';
		if ($use_my) {
			$name .= '<fb:if-is-user uid="'.$this->uid.'">My<fb:else>';
		}
		$name .= '<fb:name uid="'.$this->uid.'" linked="no" capitalize="true"';
		if ($possessive) {
			$name .= ' possessive="true"';
		}
		$name .= ($use_you) ? ' useyou="true"' : ' useyou="false"';
		if ($this->config['privacy_name'] == $this->privacy_name['first']) {
			$name .= ' firstnameonly="true"';
		}
		$name .= ' />';
		if ($use_my) {
			$name .= '</fb:else></fb:if-is-user>';
		}
		return $name;
	}
	
	/**
	 * Get the UID from an NSID.
	 * 
	 * Use this function to lookup a Facebook user from their Flickr ID.
	 * Returns a string that contains the UID if the user was found.
	 * 
	 * @author Brad Dougherty <bdougherty@gmail.com>
	 * @deprecated Use get_uid_from_nsid()
	 * @param string $nsid The Flickr ID
	 * @return int The Facebook UID
	 */
	public function get_uid($nsid) {
		return $this->get_uid_from_nsid($nsid);
	}
	
	/**
	 * Get the UID from an NSID.
	 * 
	 * Use this function to lookup a Facebook user from their Flickr ID.
	 * Returns a string that contains the UID if the user was found.
	 * 
	 * @author Brad Dougherty <bdougherty@gmail.com>
	 * @param string $nsid The Flickr ID
	 * @return int The Facebook UID
	 */
	public function get_uid_from_nsid($nsid) {
		return $this->db->get_uid_from_nsid($nsid);
	}
	
	/**
	 * Returns a proper English list from an array.
	 * 
	 * @author Brad Dougherty <bdougherty@gmail.com>
	 * @param array $array The array to implode
	 * @param string $glue The glue
	 * @param string $last The glue for the last item
	 * @return string The proper English list
	 */
	public function implode_proper($array, $glue = ', ', $last_glue = ' and ') {
		$count = count($array);
		if (!is_array($array) or $count == 0) {
			return '';
		}
		else if ($count == 1) {
			return $array[0];
		}
		else if ($count == 2) {
			return implode($last_glue, $array);
		}
		else {
			$last = array_pop($array);
			return implode($glue, $array).trim($glue).$last_glue.$last;
		}
	}
	
	/**
	 * Initialize Flickr.
	 * 
	 * This will create the Flickr object. By default it will enable the
	 * cache and check for new photos and photosets. If a user wishes to
	 * display any level of private photos, the token will be set, which
	 * causes all calls to be authenticated.
	 * 
	 * @author Brad Dougherty <bdougherty@gmail.com>
	 * @param boolean $enable_cache Enable the cache by default
	 * @param boolean $echeck_for_auth Make sure user has Flickr information first
	 * @param boolean $check_for_new Enable the check for new photos and photosets
	 * @param boolean $disable_authentication Disable authentication of the Flickr object
	 */
	public function init_flickr($enable_cache = true, $check_for_auth = true, $check_for_new = true, $disable_authentication = false) {
		
		// Make sure Flickr is authorized first
		if ($check_for_auth and !$this->is_flickr_authorized()) {
			$this->error(FlickrPhotosetsErrors::$not_linked);
		}
		
		// Initialize the Flickr object
		$this->flickr = new phpFlickr(FLICKR_API_KEY, FLICKR_API_SECRET);

		// Enable the cache by default
		if ($enable_cache) {
			$this->flickr->enableCache('fs', $this->cache_dir);
		}

		// Set the token if the filter is anything other than public
		if (($this->config['privacy_filter'] != $this->privacy_filter['public']) and !$disable_authentication) {
			$this->flickr->setToken($this->config['flickr_token']);
			if (!FlickrPhotosetsAuth::check_token($this->flickr)) {
				$this->facebook->redirect($this->app_call_url.'/auth.php?uid='.$this->uid);
			}
		}

		// Set the nsid to get it quicker
		$this->nsid = $this->config['flickr_nsid'];

		// Check for newly uploaded photos and photosets and log the results
		if ($check_for_new) {
			$this->check_for_new_photos();
			$this->check_for_new_photosets();
		}
		
	}
	
	/**
	 * Figures out whether the user has authorized their Flickr account.
	 * This will only work correctly if the user's configuration has been
	 * retrieved.
	 * 
	 * @author Brad Dougherty <bdougherty@gmail.com>
	 * @return boolean True if the user has authorized their Flickr account
	 */
	protected function is_flickr_authorized() {
		return (empty($this->config['flickr_nsid']) or empty($this->config['flickr_token'])) ? false : true;
	}
	
	/**
	 * Finds the needle in a multi-dimensional haystack.
	 * from: http://us2.php.net/manual/en/function.array-search.php#73300
	 * 
	 * @author rob at robspages dot net <rob@robspages.net>
	 * @param string $needle The string to find
	 * @param array $haystack The array to search in
	 * @return array An array indicating the position of the needle
	 */
	public function multi_array_search($needle, $haystack){
		$value = false;
		$x = 0;
		foreach($haystack as $temp){
			$search = array_search($needle, $temp);
			if (strlen($search) > 0 && $search >= 0){
				$value[0] = $x;
				$value[1] = $search;
			}
			$x++;
		}
		return $value;
	}
	
	/**
	 * Prints the google-analytics FBML tag.
	 * 
	 * @author Brad Dougherty <bdougherty@gmail.com>
	 * @deprecated Use get_analytics();
	 * @param string $page The optional page name
	 */
	final public function print_analytics($page = '') {
		echo $this->get_analytics($page);
	}
	
	/**
	 * Prints out the dashboard header.
	 * 
	 * Unfortunately, Facebook does not allow for changes to the header
	 * text of the dashboard header, so I have to print out all of the
	 * html to create it. This function prints the standard header for
	 * pages that are just for users of the app.
	 * 
	 * @author Brad Dougherty <bdougherty@gmail.com>
	 * @deprecated Use get_dashboard_header();
	 * @param string $title Additional text to add to the title
	 */
	public function print_dashboard($title = '') {
		echo $this->get_dashboard_header($title);
	}
	
	/**
	 * Prints the disclaimer paragraph.
	 * 
	 * @author Brad Dougherty <bdougherty@gmail.com>
	 * @deprecated Use get_disclaimer();
	 * @param boolean $border Add the top border and background color, defaults to false
	 */
	final public function print_disclaimer($border = false) {
		echo $this->get_disclaimer($border);
	}
	
	/**
	 * Remove photos from the array that do not match privacy settings.
	 * It will only evaluate the array if appropriate (the user has restricted
	 * which private photos they are showing).
	 *
	 * @author Brad Dougherty <bdougherty@gmail.com>
	 * @param array $photos The array of photos
	 * @return array The cleaned array
	 */
	final public function remove_photos($photos) {
		
		// Only do this if the setting is not public or private
		if (($this->config['privacy_filter'] == $this->privacy_filter['friends']) or ($this->config['privacy_filter'] == $this->privacy_filter['family']) or ($this->config['privacy_filter'] == $this->privacy_filter['friends_family'])) {
						
			// Loop through the photos
			foreach ($photos as $key=>$photo) {
				
				// Get permissions
				if (isset($photo['visibility'])) {
					$perms = $photo['visibility'];
				}
				else {
					$perms = $this->flickr->photos_getPerms($photo['id']);
				}
				
				// If the photo is public, skip
				if ($perms['ispublic']) {
					continue;
				}
				
				// Always remove private
				if (!$perms['isfriend'] and !$perms['isfamily']) {
					unset($photos[$key]);
					continue;
				}
				
				// For friends: remove family only
				if ($this->config['privacy_filter'] == $this->privacy_filter['friends']) {
					
					if (!$perms['isfriend'] and $perms['isfamily']) {
						unset($photos[$key]);
					}
					
				} // For family: remove friends only
				else if ($this->config['privacy_filter'] == $this->privacy_filter['family']) {
					
					if ($perms['isfriend'] and !$perms['isfamily']) {
						unset($photos[$key]);
					}
					
				}

			}
			
		}
		
		// Return the array
		return $photos;
		
	}
	
	/**
	 * Require these variables to exist, otherwise redirect to homepage
	 * Will expand on this function to attempt to redirect to the "nearest" page
	 *  (for example: photoset page without sid will go to the photosets page)
	 * 
	 * @author Brad Dougherty <bdougherty@gmail.com>
	 * @param $var The variable to require, followed my as many other vars as needed
	 */
	public function require_variables($var) {
		for ($i = 0, $args = func_num_args(); $i < $args; $i++) {
			$var = func_get_arg($i);
			if (empty($var)) {
				$this->facebook->redirect($this->app_url.'/index.php');
			}
		}
	}
	
	/**
	 * Rounds a number to the nearest X.
	 * from: http://marc.info/?l=php-general&m=111478099711526&w=2
	 * 
	 * @author Chris Boget <chris.boget@wild.net>
	 * @param int $number The number to round
	 * @param int $nearest The nearest number to round to
	 */
	public function round_to_nearest($number, $nearest = 5) {
		$rounded = 0;
		$mod = $number % $nearest;
		
		if ($mod >= 0) {
			$rounded = ($mod > ($nearest / 2)) ? $number + ($nearest - $mod) : $number - $mod;
		}
		else {
			$rounded = ($mod > (-$nearest / 2)) ? $number - $mod : $number + (-$nearest - $mod);
		}
		
		return $rounded;
	}
	
	/**
	 * Inserts a user's Facebook ID and session key into the database.
	 * 
	 * @author Brad Dougherty <bdougherty@gmail.com>
	 * @param int $uid The Facebook user id
	 * @param string $facebook_session_key The Facebook session key
	 * @return bool True if successfully added
	 */
	protected function set_facebook_info($facebook_uid, $facebook_session_key) {
		return $this->db->set_facebook_info($facebook_uid, $facebook_session_key);
	}
	
	/**
	 * Sets the ID of the last photo in the database.
	 * 
	 * @author Brad Dougherty <bdougherty@gmail.com>
	 * @param int $id The photo id
	 * @return boolean True if the query was successful
	 */
	private function set_last_photo($pid) {
		return $this->db->set_last_photo($this->uid, $pid);
	}
	
	/**
	 * Saves a comma separated list of the user's sets in the database.
	 * 
	 * @author Brad Dougherty <bdougherty@gmail.com>
	 * @param string $photosets A comma separated list of the set ids
	 * @return boolean True if the query was successful
	 */
	protected function set_photosets($photosets) {
		return $this->db->set_photosets($this->uid, $photosets);
	}
	
	/**
	 * Sets the user's profile FBML to the default.
	 * 
	 * @author Brad Dougherty <bdougherty@gmail.com>
	 * @param boolean $ref Whether or not to use fb:ref or just set the fbml
	 */
	protected function set_profile_fbml($ref = false) {
		if ($ref) {
			$fbml = '<fb:ref url="'.$this->app_call_url.'/profile.php?handle='.$this->uid.'" />';
		}
		else {
			$fbml = file_get_contents($this->app_call_url.'/profile.php?handle='.$this->uid);
		}
		$this->facebook->api_client->profile_setFBML($fbml, $this->uid);
	}
	
	/**
	 * Updates the user's session key in the database
	 * 
	 * @author Brad Dougherty <bdougherty@gmail.com>
	 * @param int $uid The UID
	 * @param string $session_key The current session key
	 */
	private function update_session_key($uid, $session_key) {
		if ($session_key != $facebook->fb_params['session_key']) {
			return $this->db->set_facebook_info($uid, $facebook->fb_params['session_key']);
		}
		return false;
	}
	
	/*****************************************************************
	
	The following variables are basically constants. PHP does not
	allow for constants that are arrays, so for consistency, anything
	that should be a constant is actually a variable.
	
	*****************************************************************/
	
	/**
	 * The name of the application.
	 * @var string
	 */
	public $app_name = APP_NAME;
	
	/**
	 * The canvas url.
	 * @var string
	 */
	public $app_url = APP_URL;
	
	/**
	 * The call url.
	 * @var string
	 */
	public $app_call_url = APP_CALL_URL;
	
	/**
	 * The directory for the phpFlickr cache.
	 * @var string
	 */
	public $cache_dir = CACHE_DIR;
	
	/**
	 * The number of sets to display per page.
	 * @var int
	 */
	public $sets_per_page = 6;
	
	/**
	 * The number of photos to display per page.
	 * @var int
	 */
	public $photos_per_page = 20;
	
	/**
	 * The number of photos to display per row on each page.
	 * @var int
	 */
	public $photos_per_row = 4;
	
	/**
	 * The number of photos to display on the profile by default.
	 * @var int
	 */
	public $profile_count = 5;

	/**
	 * The display types for the profile box.
	 * @var array
	 */
	public $profile_display = array(
		'last'        => 0,
		'recent'      => 1,
		'random'      => 2,
		'sets'        => 3,
		'overview'    => 4,
		'random_sets' => 5
	);
	
	/**
	 * The modes for tag filtering on the profile.
	 * @var array
	 */
	public $profile_tagmode = array(
		'any' => 0,
		'all' => 1,
	);
	
	/**
	 * The sort methods for the profile box
	 * @var array
	 */
	public $profile_sort = array(
		'date_posted_desc' => 0,
		'date_posted_asc'  => 1,
		'date_taken_desc'  => 2,
		'date_taken_asc'   => 3,
		'interesting_desc' => 4,
		'interesting_asc'  => 5,
	);
	
	/**
	 * The privacy filter options. Correspond to the values
	 * used by Flickr.
	 * @var array
	 */
	public $privacy_filter = array(
		'public'         => 1,
		'friends'        => 2,
		'family'         => 3,
		'friends_family' => 4,
		'private'        => 5,
	);
	
	/**
	 * The Facebook visibility values.
	 * @var array
	 */
	public $privacy_visibility = array(
		'all'         => 0,
		'friends'     => 1,
		'non-limited' => 2,
	);
	
	/**
	 * The name privacy values.
	 * @var array
	 */
	public $privacy_name = array(
		'full' => 0,
		'first'  => 1,
	);
	
}

?>